package com.smsc.headend.connector.netty;

import com.smsc.headend.connector.bo.RetryBo;
import com.smsc.headend.connector.enums.states.ChannelState;
import com.smsc.headend.connector.utils.NetUtil;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.protocol.ProtocolType;
import com.smsc.headend.module.task.dto.ConnectorTask;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOutboundInvoker;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;

import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.stream.Collectors;

@Slf4j
public class NettyConfigHelper {

    public static final ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    public static final String annoDevice = "anno_device";
    /**
     * 保存ConnectTask 用于重建AA后，重新执行task
     */
    public static final AttributeKey<ConnectorTask> connectTaskKey = AttributeKey.valueOf("connectTask");
    /**
     * 常规connectorTask保存
     */
    public static final AttributeKey<ConnectorTask> connectTaskRegular = AttributeKey.valueOf("connectTaskRegular");

    public static final AttributeKey<byte[][]> packetKey = AttributeKey.valueOf("rawPacket");

    public static final AttributeKey<ComEntity> comEntityAttrKey = AttributeKey.valueOf("comEntity");
    public static final AttributeKey<Boolean> isClient = AttributeKey.valueOf("isClient");

    public static final AttributeKey<ProtocolType> protocol = AttributeKey.valueOf("protocolType");

    public static final AttributeKey<RetryBo> resendPkg = AttributeKey.valueOf("resendPkg");
    // 心跳帧
    public static final AttributeKey<String> wsDHBKey = AttributeKey.valueOf("wsDHB");
    // 登陆帧
    public static final AttributeKey<String> wsDRLIKey = AttributeKey.valueOf("wsDRLI");

    public static final AttributeKey<Integer> retryTimes = AttributeKey.valueOf("retryTimes");
    public static final AttributeKey<Boolean> retryFlag = AttributeKey.valueOf("retryFlag");

    public static final AttributeKey<Long> trafficSeq = AttributeKey.valueOf("trafficSeq");
    public static final AttributeKey<Integer> receiveBytes = AttributeKey.valueOf("receiveBytes");
    public static final AttributeKey<Integer> sendBytes = AttributeKey.valueOf("sendBytes");
    public static final AttributeKey<Long> saveTimestamp =  AttributeKey.valueOf("saveTimestamp");
    public static AttributeKey<String> loginCode = AttributeKey.valueOf("loginCode");;
    public static AttributeKey<Boolean> closeBySystem = AttributeKey.valueOf("closeBySystem");

    public static AttributeKey<Boolean> statusUpdated = AttributeKey.valueOf("statusUpdated");
    public static AttributeKey<Boolean> signMsgSent = AttributeKey.valueOf("signMsgSent");
    public static AttributeKey<ScheduledFuture> retryFuture = AttributeKey.valueOf("retryFuture");

    public static AttributeKey<ChannelState> channelState = AttributeKey.valueOf("channelState");
    public static AttributeKey<Long> hbTime = AttributeKey.valueOf("hbTime");

    public static AttributeKey<byte[]> deviceLgcPkg = AttributeKey.valueOf("deviceLgcPkg");
    public static AttributeKey<Long> deviceLgcTime =  AttributeKey.valueOf("deviceLgcTime");


    public static Channel getChannelByAddress(String netAddress) {
        return NettyConfigHelper.group.stream().filter(p -> NetUtil.getIpFromChannel(p).equalsIgnoreCase(netAddress)).findFirst().orElse(null);
    }

    public static Channel getChannelByComId(Long comId) {
        return NettyConfigHelper.group.stream().filter(p -> {
            ComEntity comEntity = p.attr(comEntityAttrKey).get();
            return comEntity != null && comEntity.getComId().equals(comId);
        }).findFirst().orElse(null);
    }

    public static List<Channel> getAllChannelByModuleSN(String moduleSn) {
        return NettyConfigHelper.group.stream().filter(p -> {
            ComEntity comEntity = p.attr(comEntityAttrKey).get();
            return comEntity != null && comEntity.getModuleSn().equals(moduleSn);
        }).collect(Collectors.toList());
    }

    public static void disconnectTcp(String oldAddress) {
        log.info("initiative disconnect {}", oldAddress);
        Channel channel = getChannelByAddress(oldAddress);
        if (channel != null) {
            channel.attr(NettyConfigHelper.closeBySystem).set(true);
            removeChannel(channel);
            channel.close();
        }
    }

    public static Channel getChannelByIdAndModuleSN(ComEntity com) {
        return NettyConfigHelper.group.stream().filter(p -> {
            ComEntity comEntity = p.attr(comEntityAttrKey).get();
            return comEntity != null && comEntity.getComId().equals(com.getComId()) && comEntity.getModuleSn().equals(com.getModuleSn());
        }).findFirst().orElse(null);
    }

    public static void removeChannel(Channel channel) {
        log.info("removeChannel：id:{},addr:{},channelSize:{}", channel.id().asShortText(), NetUtil.getIpFromChannel(channel), group.size());
        group.remove(channel);
    }

    public static boolean isChannelRemovedBySystem(Channel channel) {
        Boolean removedBySystem = channel.attr(NettyConfigHelper.closeBySystem).get();
        return removedBySystem != null && removedBySystem;
    }

    public static void shutdown() {
        log.info("disconnecting all connection...");
        NettyConfigHelper.group.stream().forEach(ChannelOutboundInvoker::disconnect);
        log.info("disconnecting complete");
    }
}
